package com.cepri.service.yingxiao.baidutts;

import android.content.Context;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.text.TextUtils;

import com.baidu.tts.client.SpeechError;
import com.baidu.tts.client.SpeechSynthesizer;
import com.baidu.tts.client.SpeechSynthesizerListener;
import com.baidu.tts.client.TtsMode;
import com.cepri.service.yingxiao.ITTSListener;
import com.sgcc.pda.baselibrary.utils.LogUtil;
import com.sgcc.pda.mdrh.task.safelibrary.R;

import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

/**
 * 描述：百度语音合成播报基础类
 * 作者：jianiuqi 2020-10-07 17:37
 * 修改描述：增加回调接口方法和修改逻辑
 * 修改人：小卷毛
 * 修改版本：v1.2.1
 */
public class NonBlockSyntherizer {

    private static final int INIT = 1;
    private static final int RELEASE = 11;

    //语音播放状态
    private static final int NONE = 0;
    private static final int PLAY = 1;
    private static final int PAUSE = 2;

    //百度语音合成文件路径
    private static final String TEXT_MODEL = "bd_etts_common_text_txt_all_mand_eng_middle_big_v3.4.2_20190710.dat";
    private static final String VOICE_MALE_MODEL = "bd_etts_common_speech_m15_mand_eng_high_am-mgc_v3.6.0_20190117.dat";

    private static final String FILE_SAVE_PATH = "/baiduTTS/";
    // 请确保该PATH下有这个文件
    private static final String TEXT_FILENAME = Environment.getExternalStorageDirectory().getPath() + FILE_SAVE_PATH + TEXT_MODEL;

    // 请确保该PATH下有这个文件 ，m15是离线男声
    private static final String MODEL_FILENAME = Environment.getExternalStorageDirectory().getPath() + FILE_SAVE_PATH + VOICE_MALE_MODEL;

    private Context mContext;
    private SpeechSynthesizer mSpeechSynthesizer;
    private ITTSListener mListener;
    private HandlerThread mHandlerThread;
    private Handler mHandler;
    //语音合成是否初始化成功
    private boolean isInitied = false;
    private int playState = NONE;//合成语音播放状态
    //长文本分割
    private Map<Integer, String> textMap = new HashMap<>();

    public NonBlockSyntherizer(Context context) {
        this.mContext = context;
        initThread();
        runInHandlerThread(INIT);
    }

    /**
     * 合成并播放
     *
     * @param text 播报文本
     * @return 0表示成功
     */
    public int speak(String text) {
        if (!isInitied) {
            LogUtil.e("TTS还未初始化");
            return -1;
        }
        int result;
        if (!TextUtils.isEmpty(text) && text.length() > 30) {
            splitText(text);
            //播放第一条
            result = mSpeechSynthesizer.speak(textMap.get(0), String.valueOf(0));
        } else {
            result = mSpeechSynthesizer.speak(text);
        }

        playState = PLAY;
        if (mListener != null)
            mListener.onSpeakStart();

        return result;
    }

    /**
     * 暂停播放
     */
    public void pause() {
        if (isInitied) {
            mSpeechSynthesizer.pause();

            playState = PAUSE;
            if (mListener != null)
                mListener.onSpeakPaused();
        } else {
            LogUtil.e("TTS还未初始化");
        }
    }

    /**
     * 继续播放
     */
    public void resume() {
        if (isInitied) {
            mSpeechSynthesizer.resume();

            playState = PLAY;
            if (mListener != null)
                mListener.onSpeakResumed();
        } else {
            LogUtil.e("TTS还未初始化");
        }
    }

    /**
     * 停止合成播放
     */
    public void stop() {
        if (isInitied) {
            mSpeechSynthesizer.stop();
            textMap.clear();

            playState = NONE;
        } else {
            LogUtil.e("TTS还未初始化");
        }
    }

    /**
     * 释放语音合成资源
     */
    public void release() {
        runInHandlerThread(RELEASE);
        if (Build.VERSION.SDK_INT >= 18) {
            mHandlerThread.quitSafely();
        }
        playState = NONE;
    }

    /**
     * 初始线程
     */
    private void initThread() {
        mHandlerThread = new HandlerThread("NonBlockSyntherizer-thread");
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case INIT:
                        copyAssets(mContext, TEXT_MODEL);
                        copyAssets(mContext, VOICE_MALE_MODEL);
                        isInitied = init(mContext);
                        LogUtil.e("语音合成初始化结果：" + isInitied);
                        break;
                    case RELEASE:
                        mSpeechSynthesizer.stop();
                        mSpeechSynthesizer.release();
                        mSpeechSynthesizer = null;
                        if (Build.VERSION.SDK_INT < 18) {
                            getLooper().quit();
                        }
                        break;
                }
            }
        };
    }

    /**
     * 初始化百度语音合成
     *
     * @param context 上下文
     * @return 返回是否初始化成功
     */
    private boolean init(Context context) {
        // 1. 获取实例
        mSpeechSynthesizer = SpeechSynthesizer.getInstance();
        mSpeechSynthesizer.setContext(context);
        // 2. 设置listener
        mSpeechSynthesizer.setSpeechSynthesizerListener(mSynthesizerListener);
        // 3. 设置appId，appKey.secretKey
        mSpeechSynthesizer.setAppId(context.getString(R.string.baidu_tts_app_id));
        mSpeechSynthesizer.setApiKey(context.getString(R.string.baidu_tts_api_key), context.getString(R.string.baidu_tts_secret_key));

        // 4. 如果是纯离线SDK需要离线功能的话
        // 文本模型文件路径 (离线引擎使用)， 注意TEXT_FILENAME必须存在并且可读
        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, TEXT_FILENAME);
        // 声学模型文件路径 (离线引擎使用)， 注意TEXT_FILENAME必须存在并且可读
        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, MODEL_FILENAME);
        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, SpeechSynthesizer.MIX_MODE_DEFAULT);

        // 5. 以下setParam 参数选填。不填写则默认值生效
        // 设置在线发声音人： 0 普通女声（默认） 1 普通男声  3 情感男声<度逍遥> 4 情感儿童声<度丫丫>
        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "1");
        // 设置合成的音量，0-15 ，默认 5
        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "9");
        // 设置合成的语速，0-15 ，默认 5
        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5");
        // 设置合成的语调，0-15 ，默认 5
        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5");
        // mSpeechSynthesizer.setAudioStreamType(AudioManager.MODE_IN_CALL); // 调整音频输出
        // 纯离线sdk这个参数必填；离在线sdk没有此参数
        String sn = getTtsSN();
        mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_AUTH_SN, sn);
        // 6. 初始化
        int result = mSpeechSynthesizer.initTts(TtsMode.OFFLINE);
        return result == 0;
    }

    /**
     * 获取tts的sn码
     */
    private String getTtsSN() {
        File file = new File(Environment.getExternalStorageDirectory() + File.separator +
                "com.sgcc.pda.safework/download/TTS.json");
        BufferedReader reader = null;
        String result = "";
        try {
            reader = new BufferedReader(new FileReader(file));
            String tempString;
            // 一次读入一行，直到读入null为文件结束
            while ((tempString = reader.readLine()) != null) {
                // 显示行号
                result = result + tempString;
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
        String sn = "";
        try {
            JSONObject obj = new JSONObject(result);
            sn = obj.getString("code");
        } catch (Exception e) {
            sn = "";
        }
        return sn;
    }

    /**
     * 拷贝assets内的文件拷贝到外置存储
     *
     * @param assetsPath assets路径
     * @return 文件路径
     */
    private String copyAssets(Context context, String assetsPath) {
        // 定义拷贝到外置存储plugin文件夹
        File file = new File(Environment.getExternalStorageDirectory() + FILE_SAVE_PATH + assetsPath);
        // 创建文件
        if (!file.exists()) {
            file.getParentFile().mkdirs();
        }
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            // 读取assets文件
            is = context.getAssets().open(assetsPath);
            // 文件输出流
            fos = new FileOutputStream(file);
            file.createNewFile();
            byte[] bytes = new byte[1024];
            int len;
            while ((len = is.read(bytes)) > 0) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.flush();
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        LogUtil.e(":" + file.getPath());
        return file.getPath();
    }

    private void runInHandlerThread(int action) {
        Message msg = Message.obtain();
        msg.what = action;
        mHandler.sendMessage(msg);
    }

    /**
     * 是否完成初始化
     */
    public boolean isInitDone() {
        return isInitied;
    }

    /**
     * 获取播放状态
     *
     * @return 0未开始 1开始 2暂停
     */
    public int getPlayState() {
        return playState;
    }

    /**
     * 语音播报监听
     */
    public void setPlayListener(ITTSListener listener) {
        this.mListener = listener;
    }

    /**
     * 语音合成进度监听
     */
    SpeechSynthesizerListener mSynthesizerListener = new SpeechSynthesizerListener() {

        /**
         * 本次合成过程开始时，SDK的回调
         *
         * @param utteranceId 调用speak或者synthesize合成方法时输入的，默认是"0"
         */
        @Override
        public void onSynthesizeStart(String utteranceId) { }

        /**
         * 合成数据过程中的回调，返回合成数据和进度，分多次回调
         *
         * @param utteranceId 调用speak或者synthesize合成方法时输入的，默认是"0"
         * @param audioData  合成的部分数据，可以就这部分数据自行播放或者顺序保存到文件。
         *                   如果保存到文件的话，是一个pcm可以播放的音频文件。
         *                   音频数据是16K采样率，16bits编码，单声道
         * @param progress 大致进度。从0 到 “合成文本的字符数”。
         * @param engineType 1: 当前的audioData数据由离线引擎合成；
         *                   0：当前的audioData数据数据由在线引擎（百度服务器）合成
         */
        @Override
        public void onSynthesizeDataArrived(String utteranceId, byte[] audioData, int progress, int engineType) { }

        /**
         * 本次合成正常结束状态时，SDK的回调
         *
         * @param utteranceId 调用speak或者synthesize合成方法时输入的，默认是"0"
         */
        @Override
        public void onSynthesizeFinish(String utteranceId) { }

        /**
         * SDK开始控制播放器播放合成的声音。
         * 如果使用speak方法会有此回调，使用synthesize则没有。
         *
         * @param utteranceId 调用speak时输入的，默认是"0"
         */
        @Override
        public void onSpeechStart(String utteranceId) { }

        /**
         * 播放数据过程中的回调接口，分多次回调。
         * 如果使用speak方法会有此回调，使用synthesize则没有。
         *
         * @param utteranceId 调用speak时输入的，默认是"0"
         * @param progress 大致进度。从0 到 “合成文本的字符数”。
         */
        @Override
        public void onSpeechProgressChanged(String utteranceId, int progress) { }

        /**
         * 播放正常结束状态时的回调方法，
         * 如果过程中出错，则回调onError，不再回调此接口。
         *
         * @param utteranceId 调用speak时输入的，默认是"0"
         */
        @Override
        public void onSpeechFinish(String utteranceId) {
            playState = NONE;
            try {
                int lastTextIndex = Integer.parseInt(utteranceId);
                // 播放完成
                if (lastTextIndex >= textMap.size() - 1) {
                    if (mListener != null)
                        mListener.onCompleted();

                    return;
                }

                mSpeechSynthesizer.speak(
                        textMap.get(lastTextIndex + 1),
                        String.valueOf(lastTextIndex + 1)
                );
            } catch (Exception e) {
                LogUtil.e("数字转换失败");
            }
        }

        /**
         * 合成和播放过程中出错时回调此接口
         *
         * @param utteranceId 调用speak或者synthesize合成方法时输入的，默认是"0"
         * @param error SpeechError 类有2个值：
         *                 code：int，错误码。 具体错误码见“错误码及解决方法”一节
         *                 description： String, 具体的错误信息
         */
        @Override
        public void onError(String utteranceId, SpeechError error) { }
    };

    /**
     * 分割字符串
     */
    private void splitText(String text) {
        textMap.clear();
        String[] after = text.split("[,.?:!，。？！：]");
        for (int i = 0; i < after.length; i++) {
            textMap.put(i, after[i]);
        }
    }

}
